package org.ccay.audit.service.impl;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;

import javax.inject.Inject;
import javax.inject.Named;

import org.aopalliance.intercept.MethodInvocation;
import org.ccay.core.log.Audit;
import org.ccay.core.log.AuditContext;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.IRequestContext;
import org.ccay.core.request.IUserPrincipal;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.util.JsonUtil;
import org.springframework.util.ClassUtils;

/**
 * 审计日志拦截器。
 * 
 * Created：2011-9-8
 * @author chaowangbang
 * @version:
 */
@Named
public class AuditInterceptor extends LogAspectSupport {
	
	private final ILogger logger = CcayLoggerFactory.getLogger(AuditInterceptor.class);
	
	/** 常量，用来标识请求上下文中的一个属性 **/
	private static final String SKIP_AUDIT= "SKIP_AUDIT";

	@Inject
	private AuditLogProducer auditService;

	
	/* (non-Javadoc)
	 * @see org.ccay.log.service.impl.internal.LogAspectSupport#execute(org.aopalliance.intercept.MethodInvocation, java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	@Override
	protected Object execute(MethodInvocation invocation, Object target,
			Method method, Object[] args) throws Throwable {
		/** 调用业务方法 **/
		Date startDate = new Date();
		Object result = invocation.proceed();
		Date endDate = new Date();
		
		IRequestContext ctx = RequestContext.getCurrent();
		Object skipAudit = ctx.getItem(SKIP_AUDIT);
		if(skipAudit!=null && ((Boolean)skipAudit).booleanValue()) {//忽略审计
			return result;
		}
		
		
		/** 记录审计日志 **/
		try {
			AuditContext auditContext = wrapAuditContext(invocation, ctx, target, args);
			auditContext.setStartDate(startDate);
			auditContext.setEndDate(endDate);
			auditService.audit(auditContext);
			
			Audit audit = method.getAnnotation(Audit.class);
			if(audit!=null && audit.skipLatter()) {
				// 置标志位：跳过本次操作的后续审计
				ctx.setItem(SKIP_AUDIT, Boolean.TRUE); 
			}
		} catch (Exception e) {
			// 记录审计日志出错时，不应该影响业务逻辑的正常执行，所以这里仅仅只是记录一下错误日志
			logger.error(e);
		}

		return result;
	}
	
	/**
	 * 封装审计上下文。
	 * 
	 * @param invocation
	 * @param reqCtx
	 * @param target
	 * @param args
	 * @return
	 */
	private AuditContext wrapAuditContext(MethodInvocation invocation, IRequestContext reqCtx, Object target, Object[] args) {
		Method m = ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass());
		
		AuditContext auditContext = new AuditContext(target, m, args);

		IUserPrincipal user = reqCtx.getUser();
		//用户不存在时，为审计上下文设置默认用户，角色ID设置为空，否则设置用户账号和当前角色ID
		if(user == null){
			auditContext.setUserId(IUserPrincipal.ANONYMOUS_USERID);
			auditContext.setDimension("{}");
		} else {
			auditContext.setUserId(user.getUserId());
			String dimensionStr = "{}";
			if(null != user.getCurrentDimensions()){
				try {
					JsonUtil.getJsonString(user.getCurrentDimensions());
				} catch (IOException e) {
					logger.debug("Can not covent Dimension " +user.getCurrentDimensions() +" to JSON.");
				}
			}
			auditContext.setDimension(dimensionStr);
		}
		auditContext.setCurrentUserIP(reqCtx.getUserIp());
		try {
			auditContext.setServerName(InetAddress.getLocalHost().getHostName());
		} catch (UnknownHostException e) {
			auditContext.setServerName("UnknownHost");
		}
		return auditContext;
	}

}
